package com.neusoft.databus.agent.pool.serial;

import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.neusoft.databus.common.connection.AbstractConnection;
import com.neusoft.databus.common.connection.SerialConnection;
import com.neusoft.databus.common.exception.DisconnectException;
import com.neusoft.databus.common.exception.RetryableException;
import com.neusoft.databus.common.pool.Connector;

import gnu.io.SerialPort;
import lombok.extern.slf4j.Slf4j;

/**
 * Serial串口连接器
 *
 * @author naxiang
 */
@Slf4j
@Component
public class SerialConnector implements Connector<SerialPort> {
    @Autowired
    private SerialPool pool;

    final Map<String, ReadWriteLock> lockMap = new ConcurrentHashMap<>();

    @Override
    public SerialPort getConn(final AbstractConnection connection) throws Exception {
        return this.getConn(connection, 0);
    }

    @Override
    public SerialPort getConn(final AbstractConnection connection, final int timeout) throws Exception {

        final SerialConnection serialConnection = (SerialConnection) connection;
        final String lockId = serialConnection.getComValue();
        SerialPort serialPort = null;
        try {
            synchronized (SerialConnector.class) {
                ReadWriteLock lock = this.lockMap.get(lockId);
                if (null == lock) {
                    lock = new ReentrantReadWriteLock(true);
                    this.lockMap.put(lockId, lock);
                }
            }
            final ReadWriteLock lock = this.lockMap.get(lockId);
            lock.writeLock().lock();
            if (timeout == 0) {
                serialPort = this.pool.borrowObject(serialConnection);
            } else {
                serialPort = this.pool.borrowObject(serialConnection, timeout * 1000);
            }
        } catch (final NoSuchElementException e) {
            throw new RetryableException(e.getMessage(), e);
        } catch (final Exception e) {
            // 通过对象池Factory创建新对象时，抛出的所有异常，都会在这里捕捉到。
            // 封装成中断异常
            throw new DisconnectException(e.getMessage(), e);
        } finally {
            final ReadWriteLock lock = this.lockMap.get(lockId);
            lock.writeLock().unlock();
        }
        return serialPort;
    }

    @Override
    public void returnConn(final AbstractConnection connection, final SerialPort conn) {
        try {
            this.pool.returnObject((SerialConnection) connection, conn);
        } catch (final Exception e) {
            SerialConnector.log.warn("归还Socket对象时错误, {}", e.getMessage());
        }
    }

    @Override
    @Deprecated
    public void invalidateConn(final AbstractConnection connection, final SerialPort conn) {
        try {
            this.pool.invalidateObject((SerialConnection) connection, conn);
        } catch (final Exception e) {
            SerialConnector.log.error(e.getMessage(), e);
        }
    }

    @Override
    @Deprecated
    public void removeAllConns(final AbstractConnection connection) {
        try {
            this.pool.clear((SerialConnection) connection);
        } catch (final Exception e) {
            SerialConnector.log.error(e.getMessage(), e);
        }

    }

}
